package borderPatrol.misc;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.LinkedList;

import borderPatrol.Calculators.TreeUpdater;
import borderPatrol.Data.Move;
import borderPatrol.Data.SimulationBoard;

/**
 * Class storing game tree for CFR
 * 
 * @author Jiri Cermak
 * 
 */
public class GameTreeBuilder {

	private TreeUpdater calculator;

	/**
	 * HashMap containing all information sets of game tree
	 */
	private HashMap<String, CFRInformationSet> gameTree;

	public GameTreeBuilder(TreeUpdater calculator) {
		this.calculator = calculator;
	}

	/**
	 * If available returns hashmap with information sets, if not creates one
	 * 
	 * @return hashmap with information sets
	 */
	public HashMap<String, CFRInformationSet> getGameTree() {

		if (gameTree == null) {
			try {
				loadGameTree("1000IterationsTree");
			} catch (FileNotFoundException e) {

				System.out.println("Game tree file not found. Generating new one...");

				gameTree = new HashMap<String, CFRInformationSet>(1000000);

				buildCompleteTree(new SimulationBoard(3, 3), (byte) 1);
				buildCompleteTree(new SimulationBoard(3, 3), (byte) 2);

				calculator.setGameTree(gameTree);

				calculator.updateTree(1000);

				saveGameTree("1000IterationsTree");

			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}

		return this.gameTree;
	}

	/**
	 * Method which fills hashMap informationSets with all information sets
	 * 
	 * @param board
	 * @param sign
	 */
	@SuppressWarnings("unchecked")
	private void buildCompleteTree(SimulationBoard board, byte sign) {

		if (board.isPoacherOnFinalPosition()) {
			gameTree.put(board.getIdForPlayer(sign), new CFRInformationSet(board.getActionsForPlayer(sign), 1, new LinkedList<Integer>(), board.getPossibleHistoriesForPlayer(3 - sign).size()));
		} else if (board.getRound() > 6) {
			gameTree.put(board.getIdForPlayer(sign), new CFRInformationSet(board.getActionsForPlayer(sign), 2, getPoachersWinningCoordinates(board, sign), board.getPossibleHistoriesForPlayer(3 - sign).size())); // pozici
		} else {

			LinkedList<Move> moves = (LinkedList<Move>) board.getActionsForPlayer(sign).clone();
			LinkedList<History> possibleHistories = Cloner.cloneHistories(board.getPossibleHistoriesForPlayer(3 - sign));

			int[] oldPosition = board.getPositionForPlayer(sign).clone();

			if (gameTree.get(board.getIdForPlayer(sign)) == null) {
				gameTree.put(board.getIdForPlayer(sign), new CFRInformationSet(board.getActionsForPlayer(sign), (byte) 0, possibleHistories));
			}

			for (Move successor : moves) {

				board.makeSingleMove(successor, sign);

				buildCompleteTree(board, sign);

				board.reverseToPosition(oldPosition, possibleHistories, sign);

			}
		}
	}

	private LinkedList<Integer> getPoachersWinningCoordinates(SimulationBoard board, byte sign) {
		LinkedList<Integer> poachersWinningCoordinates = new LinkedList<Integer>();

		if (sign == 2) {
			for (int i = 0; i < board.getPossibleHistoriesForPlayer(3 - sign).size(); i++) {
				if (Judge.getInstance().isPoacherOnFinalPosition(board.getPossibleHistoriesForPlayer(1).get(i).sequence.peek())) {
					poachersWinningCoordinates.add(i);
				}
			}

		}
		return poachersWinningCoordinates;
	}

	/**
	 * Saves created game tree to file
	 * 
	 * @param fileName
	 */
	private void saveGameTree(String fileName) {

		File file = new File(fileName);
		FileOutputStream f;

		try {
			f = new FileOutputStream(file);
			ObjectOutputStream s = new ObjectOutputStream(new BufferedOutputStream(f));
			s.writeObject(gameTree);

			s.close();
			f.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Loads previously generated game tree
	 * 
	 * @param fileName
	 * @throws FileNotFoundException
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	private void loadGameTree(String fileName) throws FileNotFoundException, IOException, ClassNotFoundException {

		File file = new File(fileName);
		FileInputStream f;

		f = new FileInputStream(file);

		ObjectInputStream s = new ObjectInputStream(new BufferedInputStream(f));

		gameTree = (HashMap<String, CFRInformationSet>) s.readObject();

		s.close();
		f.close();

	}

}
